package com.breezelin.taglineview;

/*
 * Created by Breeze Lin
 * 2018/7/30 14:09
 * 1linyufeng1@gmail.com
 */

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
 * 文字列队控件
 * <p>
 * 产生自展示tag类效果的需求。
 * </p>
 */
public class TagLineView extends View {

    private int tagSpacing; // tag控件的间距
    private int tagLineSpacing; // tag控件的行距
    private int tagTextSize; // tag控件的字号
    private int tagBgCorner; // tag控件的圆角
    private int[] tagPadding; // tag控件的padding
    private int tagDefTextColor; // tag的文字颜色，数组中没有即取默认
    private List<Integer> tagTextColor; // tag的文字颜色
    private int tagDefBgColor; // tag的背景颜色，数组中没有即取默认
    private List<Integer> tagBgColor; // tag的背景颜色
    private List<String> tagText; // tag的文字内容

    private List<Integer> lineCounter; // 用于绘制超长字串的文字计数器
    private Paint textPaint;
    private Paint textBgPaint;
    private Rect textRect;
    private RectF drawRect;
    private Paint.FontMetricsInt textFontMetrics;

    public TagLineView(Context context) {
        super(context);
        init(null);
    }

    public TagLineView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public TagLineView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttributeSet attrs) {

        tagSpacing = 0;
        tagLineSpacing = 0;
        tagTextSize = 12; // 默认字号12
        tagPadding = new int[4];
        tagTextColor = new LinkedList<>();
        tagBgColor = new LinkedList<>();
        tagText = new LinkedList<>();
        lineCounter = new LinkedList<>();
        textPaint = new Paint();
        textBgPaint = new Paint();
        textRect = new Rect();
        drawRect = new RectF();
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tagTextSize, getContext().getResources().getDisplayMetrics()));
        textFontMetrics = new Paint.FontMetricsInt();

        if (isInEditMode()) {
            // 编辑模式下，默认展示
            tagSpacing = 6;
            tagLineSpacing = 4;
            tagPadding = new int[]{5, 5, 5, 5};
            tagDefBgColor = Color.BLACK;
            tagDefTextColor = Color.WHITE;
            tagTextSize = 12;
            Collections.addAll(this.tagText, "tag a", " tag b", "tag c", "tag d", "tag e");
        }

        if (null != attrs) {
            TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.TagLineView);
            tagSpacing = ta.getDimensionPixelSize(R.styleable.TagLineView_tlvTagSpacing, tagSpacing);
            tagLineSpacing = ta.getDimensionPixelSize(R.styleable.TagLineView_tlvTagLineSpacing, tagLineSpacing);
            tagPadding[0] = ta.getDimensionPixelSize(R.styleable.TagLineView_tlvTagPadding, tagPadding[0]);
            tagPadding[1] = ta.getDimensionPixelSize(R.styleable.TagLineView_tlvTagPadding, tagPadding[1]);
            tagPadding[2] = ta.getDimensionPixelSize(R.styleable.TagLineView_tlvTagPadding, tagPadding[2]);
            tagPadding[3] = ta.getDimensionPixelSize(R.styleable.TagLineView_tlvTagPadding, tagPadding[3]);
            tagPadding[0] = ta.getDimensionPixelSize(R.styleable.TagLineView_tlvTagPaddingLeft, tagPadding[0]);
            tagPadding[1] = ta.getDimensionPixelSize(R.styleable.TagLineView_tlvTagPaddingTop, tagPadding[1]);
            tagPadding[2] = ta.getDimensionPixelSize(R.styleable.TagLineView_tlvTagPaddingRight, tagPadding[2]);
            tagPadding[3] = ta.getDimensionPixelSize(R.styleable.TagLineView_tlvTagPaddingBottom, tagPadding[3]);
            tagBgCorner = ta.getDimensionPixelSize(R.styleable.TagLineView_tlvTagCornerRadius, tagBgCorner);
            tagDefTextColor = ta.getColor(R.styleable.TagLineView_tlvTagTextColor, tagDefTextColor);
            tagDefBgColor = ta.getColor(R.styleable.TagLineView_tlvTagBgColor, tagDefBgColor);
            tagTextSize = (int) ta.getDimension(R.styleable.TagLineView_tlvTagTextSize, tagTextSize);
            ta.recycle();
        }
    }

    /**
     * 设置标签，并配置每个的颜色
     *
     * @param tagTextColor 标签字体颜色
     * @param tagBgColor   标签背景颜色
     * @param tagText      标签内容
     */
    public void setTags(Integer[] tagTextColor, Integer[] tagBgColor, String[] tagText) {
        this.tagTextColor.clear();
        Collections.addAll(this.tagTextColor, tagTextColor);
        this.tagBgColor.clear();
        Collections.addAll(this.tagBgColor, tagBgColor);
        this.tagText.clear();
        Collections.addAll(this.tagText, tagText);
        requestLayout(); // 重新测量，以容纳新的tag组
        invalidate(); // 重新绘制
    }

    /**
     * 设置标签，颜色由xml中进行配置。
     *
     * @param tagText 标签内容
     */
    public void setTags(String[] tagText) {
        this.tagTextColor.clear();
        this.tagBgColor.clear();
        this.tagText.clear();
        Collections.addAll(this.tagText, tagText);
        requestLayout(); // 重新测量，以容纳新的tag组
        invalidate(); // 重新绘制
    }

    /**
     * 配置一些标签的属性
     *
     * @param tagSpacing     标签的横向间距
     * @param tagLineSpacing 标签的行距
     * @param tagTextSize    标签字体大小
     * @param tagBgCorner    标签背景的圆角
     */
    public void setTagDimensions(int tagSpacing, int tagLineSpacing, int tagTextSize, int tagBgCorner, int leftPadding, int topPadding, int rightPadding, int bottomPadding) {
        this.tagSpacing = tagSpacing;
        this.tagLineSpacing = tagLineSpacing;
        this.tagTextSize = tagTextSize;
        this.tagBgCorner = tagBgCorner;
        tagPadding[0] = leftPadding;
        tagPadding[1] = topPadding;
        tagPadding[2] = rightPadding;
        tagPadding[3] = bottomPadding;
        textPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tagTextSize, getContext().getResources().getDisplayMetrics()));
        requestLayout(); // 重新测量，以容纳新的数字
        invalidate(); // 重新绘制
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        // 父控件提供的宽高
        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);

        // 计算需要的高度
        int height = 0; // 累计的高度
        int widthAddition = 0; // 当前行叠加的宽度
        int textWidth = 0; // 当前文字序列的宽度，加入了padding和spacing
        int textHeight = 0; // 文字标准高度，以中文方块字为标准，加入padding和spacing
        for (String s : tagText) {
            if (textHeight == 0) {
                textPaint.getTextBounds("文字", 0, 2, textRect);
                textHeight = textRect.height() + tagPadding[1] + tagPadding[3] + tagLineSpacing;
            }
            textPaint.getTextBounds(s, 0, s.length(), textRect);
            textWidth = textRect.width() + tagPadding[0] + tagPadding[2] + tagSpacing;
            if (widthAddition + textWidth < parentWidth) {
                // 当前行可以容纳文字序列
                // 在第一行里，要手动增加一行的高度
                if (widthAddition == 0) {
                    height += textHeight;
                }
                // 将当前文字序列的宽度叠加上去
                widthAddition += textWidth;
            } else {
                // 当前行不可容纳文字序列，则行宽度需要重置，高度需要叠加
                if (textWidth > parentWidth) {
                    // 超过一行时，会将字串切成一行一行的进行展示
                    // TODO: 2018/7/30 现在使用一种迫近的方式来进行超长字串的测量，如果有好的算法，希望修改
                    // TODO: 2018/7/31 此计算追求速度，长度是不精确的，因为没有以字为单位切割字串，如果需要精确处理，则应当与绘制处的测算进行统一
                    // TODO: 2018/7/30 如果需求中有"一个tag最多展示为一行"之类的要求，此处也要修改
                    int lines = textRect.width() / parentWidth;
                    do {
                        // 每行都需要一套spacing+padding
                        textWidth = textRect.width() + (tagPadding[0] + tagPadding[2] + tagSpacing) * (lines + 1);
                        // 如果当前算得的宽度，除以可布局宽度后的结果大于预测的行数，则叠加需要的行数并进行重算
                    } while (lines++ < textWidth / parentWidth);
                    // 行数计算完成，字串测算完成
                    widthAddition = 0;
                    height += textHeight * (textWidth / parentWidth + 2);
                } else {
                    widthAddition = textWidth;
                    height += textHeight;
                }
            }
        }

        // 宽度根据父控件而定，高度则需要根据文字序列进行计算
        setMeasuredDimension(getDefaultSize(parentWidth, widthMeasureSpec),
                getDefaultSize(height, heightMeasureSpec));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 绘制文字
        int parentWidth = getWidth();
        int height = 0;
        int widthAddition = 0; // 当前行叠加的宽度
        int textWidth = 0; // 当前文字序列的宽度，加入了padding和spacing
        int textHeight = 0; // 文字标准高度，以中文方块字为标准，加入padding和spacing
        for (int i = 0; i < tagText.size(); i++) {
            String s = tagText.get(i);
            if (textHeight == 0) {
                textPaint.getTextBounds("文字", 0, 2, textRect);
                textHeight = textRect.height() + tagPadding[1] + tagPadding[3] + tagLineSpacing;
                textPaint.getFontMetricsInt(textFontMetrics);
            }
            textPaint.getTextBounds(s, 0, s.length(), textRect);
            textWidth = textRect.width() + tagPadding[0] + tagPadding[2] + tagSpacing;
            // 当前行不可容纳文字序列，则行宽度需要重置，高度需要叠加
            if (widthAddition + textWidth > parentWidth) {
                height += textHeight; // 只增加一行，之后的在绘制中添加
                widthAddition = 0;
            }
            // TODO: 2018/7/30 文字的纵向位置还需要特别处理，没有很精确
            if (textWidth < parentWidth) {
                // 绘制背景
                if (null == tagBgColor || i >= tagBgColor.size()) {
                    textBgPaint.setColor(tagDefBgColor);
                } else {
                    textBgPaint.setColor(tagBgColor.get(i));
                }
                drawRect.set(widthAddition + (tagSpacing >> 1),
                        height + (tagLineSpacing >> 1),
                        widthAddition + textWidth - (tagSpacing >> 1),
                        height + textHeight - (tagLineSpacing >> 1));
                canvas.drawRoundRect(drawRect, tagBgCorner, tagBgCorner, textBgPaint);
                // 绘制文字
                if (null == tagTextColor || i >= tagTextColor.size()) {
                    textPaint.setColor(tagDefTextColor);
                } else {
                    textPaint.setColor(tagTextColor.get(i));
                }
                drawRect.set(widthAddition + (tagSpacing >> 1) + tagPadding[0],
                        height + (tagLineSpacing >> 1) + tagPadding[1],
                        widthAddition + textWidth - (tagSpacing >> 1) - tagPadding[2],
                        height + textHeight - (tagLineSpacing >> 1) - tagPadding[3]);
                canvas.drawText(s, drawRect.left, drawRect.top - textFontMetrics.ascent, textPaint);
                // 将当前文字序列的宽度叠加上去
                widthAddition += textWidth % parentWidth;
            } else {
                // 顺次绘制超出一行的文字序列
                int index = 0;
                int bgTop = height; // 记录绘制背景的起点
                height += tagPadding[1] + (tagSpacing >> 1);
                int currentLineWidth = 0;
                // 粗略估算文字序列的行数
                int lines = textWidth / parentWidth + 1;
                // 假设字串的字符宽度分布基本均匀，估算出用于展示一行的字数
                int textPerLine = s.length() / lines;
                // 尝试用于换行的点，然后向正确的方向移动光标
                textPaint.getTextBounds(s, index, index + textPerLine, textRect);
                while (index < s.length() - 1) {
                    // 确认换行点
                    currentLineWidth = textRect.width() + tagPadding[0] + tagPadding[1] + tagSpacing;
                    if (s.length() > index + textPerLine) { // 看看是不是已经画到最后一行
                        if (currentLineWidth > parentWidth) {
                            do {
                                textPerLine--; // 同时调整一行长度的预估值
                                textPaint.getTextBounds(s, index, index + textPerLine, textRect);
                                currentLineWidth = textRect.width() + tagPadding[0] + tagPadding[1] + tagSpacing;
                            } while (currentLineWidth > parentWidth);
                        } else if (currentLineWidth <= parentWidth) {
                            do {
                                textPerLine++; // 同时调整一行的预估值
                                textPaint.getTextBounds(s, index, index + textPerLine, textRect);
                                currentLineWidth = textRect.width() + tagPadding[0] + tagPadding[1] + tagSpacing;
                            } while (currentLineWidth < parentWidth);
                            textPerLine--;
                        }
                    } else {
                        textPerLine = s.length() - index; // 最后一行
                    }
                    lineCounter.add(textPerLine);
                    index += textPerLine;
                    height += textHeight - tagPadding[1] - tagPadding[3]; // 换行
                }
                // 绘制背景
                if (null == tagBgColor || i >= tagBgColor.size()) {
                    textBgPaint.setColor(tagDefBgColor);
                } else {
                    textBgPaint.setColor(tagBgColor.get(i));
                }
                drawRect.set((tagSpacing >> 1), bgTop + (tagLineSpacing >> 1), getWidth() - (tagSpacing >> 1), height + tagPadding[3] - tagLineSpacing);
                canvas.drawRoundRect(drawRect, tagBgCorner, tagBgCorner, textBgPaint);
                // 重置index，开始绘制文字
                index = 0;
                bgTop += tagPadding[0] + (tagSpacing >> 1); // 借用这个值
                if (null == tagTextColor || i >= tagTextColor.size()) {
                    textPaint.setColor(tagDefTextColor);
                } else {
                    textPaint.setColor(tagTextColor.get(i));
                }
                // 绘制文字
                for (int j = 0; j < lineCounter.size(); j++) {
                    textPaint.getTextBounds(s, index, index + lineCounter.get(j), textRect);
                    textWidth = textRect.width() + tagPadding[0] + tagPadding[2] + tagSpacing;
                    // 绘制一行
                    drawRect.set((tagSpacing >> 1) + tagPadding[0], bgTop, textWidth - (tagSpacing >> 1) - tagPadding[2], bgTop + textHeight);
                    canvas.drawText(s.substring(index, index + lineCounter.get(j)), drawRect.left, drawRect.top - textFontMetrics.ascent, textPaint);
                    index += lineCounter.get(j);
                    if (j != lineCounter.size() - 1) {
                        bgTop += textHeight - tagPadding[1] - tagPadding[3]; // 换行
                    }
                }
                lineCounter.clear(); // 清空记录
                height += tagPadding[3] - (tagSpacing >> 1); // 高度规制
                widthAddition = 0; // 重置当前行的宽度，避免换行
            }
        }
    }
}
